Allow cargo:rustc-env in build scripts
authorKarol Kuczmarski <karol.kuczmarski@gmail.com>
Mon, 17 Apr 2017 18:48:56 +0000 (19:48 +0100)
committerKarol Kuczmarski <karol.kuczmarski@gmail.com>
Fri, 12 May 2017 19:18:18 +0000 (20:18 +0100)
src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_rustc/custom_build.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/util/machine_message.rs
tests/build-script.rs

index 1256787f4f8aa1ea12bce637ffc9db00339a0860..ef5aa927499e98449357cc2e379a59db23c8b364 100644 (file)
@@ -670,6 +670,7 @@ fn scrape_target_config(config: &Config, triple: &str)
             library_paths: Vec::new(),
             library_links: Vec::new(),
             cfgs: Vec::new(),
+            env: Vec::new(),
             metadata: Vec::new(),
             rerun_if_changed: Vec::new(),
             warnings: Vec::new(),
index 4c1dca592fe97c5f35dabf4347e76c6a3923fb46..64f05f918e891fd9550d181faade997436273e80 100644 (file)
@@ -21,6 +21,8 @@ pub struct BuildOutput {
     pub library_links: Vec<String>,
     /// Various `--cfg` flags to pass to the compiler
     pub cfgs: Vec<String>,
+    /// Additional environment variables to run the compiler with.
+    pub env: Vec<(String, String)>,
     /// Metadata to pass to the immediate dependencies
     pub metadata: Vec<(String, String)>,
     /// Paths to trigger a rerun of this build script.
@@ -255,6 +257,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>)
                 linked_libs: &parsed_output.library_links,
                 linked_paths: &library_paths,
                 cfgs: &parsed_output.cfgs,
+                env: &parsed_output.env,
             });
         }
 
@@ -321,6 +324,7 @@ impl BuildOutput {
         let mut library_paths = Vec::new();
         let mut library_links = Vec::new();
         let mut cfgs = Vec::new();
+        let mut env = Vec::new();
         let mut metadata = Vec::new();
         let mut rerun_if_changed = Vec::new();
         let mut warnings = Vec::new();
@@ -361,6 +365,7 @@ impl BuildOutput {
                 "rustc-link-lib" => library_links.push(value.to_string()),
                 "rustc-link-search" => library_paths.push(PathBuf::from(value)),
                 "rustc-cfg" => cfgs.push(value.to_string()),
+                "rustc-env" => env.push(BuildOutput::parse_rustc_env(value, &whence)?),
                 "warning" => warnings.push(value.to_string()),
                 "rerun-if-changed" => rerun_if_changed.push(value.to_string()),
                 _ => metadata.push((key.to_string(), value.to_string())),
@@ -371,6 +376,7 @@ impl BuildOutput {
             library_paths: library_paths,
             library_links: library_links,
             cfgs: cfgs,
+            env: env,
             metadata: metadata,
             rerun_if_changed: rerun_if_changed,
             warnings: warnings,
@@ -407,6 +413,17 @@ impl BuildOutput {
         }
         Ok((library_paths, library_links))
     }
+
+    pub fn parse_rustc_env(value: &str, whence: &str)
+                           -> CargoResult<(String, String)> {
+        let mut iter = value.splitn(2, '=');
+        let name = iter.next();
+        let val = iter.next();
+        match (name, val) {
+            (Some(n), Some(v)) => Ok((n.to_owned(), v.to_owned())),
+            _ => bail!("Variable rustc-env has no value in {}: {}", whence, value),
+        }
+    }
 }
 
 /// Compute the `build_scripts` map in the `Context` which tracks what build
index 439046eb1230ae23d2ae97da5ec45137cd71a9d4..9d9c56d2c1977cf06da06687b777accaaf0a8a2b 100644 (file)
@@ -305,12 +305,15 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc<Executor>) -> CargoResult<Work
         // also need to be sure to add any -L paths for our plugins to the
         // dynamic library load path as a plugin's dynamic library may be
         // located somewhere in there.
+        // Finally, if custom environment variables have been produced by
+        // previous build scripts, we include them in the rustc invocation.
         if let Some(build_deps) = build_deps {
             let build_state = build_state.outputs.lock().unwrap();
             add_native_deps(&mut rustc, &build_state, &build_deps,
                                  pass_l_flag, &current_id)?;
             add_plugin_deps(&mut rustc, &build_state, &build_deps,
                                  &root_output)?;
+            add_custom_env(&mut rustc, &build_state, &build_deps, &current_id)?;
         }
 
         // FIXME(rust-lang/rust#18913): we probably shouldn't have to do
@@ -420,6 +423,26 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc<Executor>) -> CargoResult<Work
         }
         Ok(())
     }
+
+    // Add all custom environment variables present in `state` (after they've
+    // been put there by one of the `build_scripts`) to the command provided.
+    fn add_custom_env(rustc: &mut ProcessBuilder,
+                      build_state: &BuildMap,
+                      build_scripts: &BuildScripts,
+                      current_id: &PackageId) -> CargoResult<()> {
+        for key in build_scripts.to_link.iter() {
+            let output = build_state.get(key).chain_error(|| {
+                internal(format!("couldn't find build state for {}/{:?}",
+                                 key.0, key.1))
+            })?;
+            if key.0 == *current_id {
+                for &(ref name, ref value) in output.env.iter() {
+                    rustc.env(name, value);
+                }
+            }
+        }
+        Ok(())
+    }
 }
 
 /// Link the compiled target (often of form foo-{metadata_hash}) to the
index 1d4f33a86da9bbfdac131b0a1be3d24ec9008fcb..5f917f85a74a868dc60102ea4ee0fa93a59217cc 100644 (file)
@@ -48,6 +48,7 @@ pub struct BuildScript<'a> {
     pub linked_libs: &'a [String],
     pub linked_paths: &'a [String],
     pub cfgs: &'a [String],
+    pub env: &'a [(String, String)],
 }
 
 impl<'a> Message for BuildScript<'a> {
index 8d40474d217ad31a20ea6b4a8c20994484f8530d..e663f5abc923d87cd414f52a2dac66b0614e5467 100644 (file)
@@ -1575,6 +1575,82 @@ fn cfg_override_doc() {
     assert_that(&p.root().join("target/doc/bar/fn.bar.html"), existing_file());
 }
 
+#[test]
+fn env_build() {
+    let build = project("builder")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "builder"
+            version = "0.0.1"
+            authors = []
+            build = "build.rs"
+        "#)
+        .file("src/main.rs", r#"
+            const FOO: &'static str = env!("FOO");
+            fn main() {}
+        "#)
+        .file("build.rs", r#"
+            fn main() {
+                println!("cargo:rustc-env=FOO=foo");
+            }
+        "#);
+    assert_that(build.cargo_process("build").arg("-v"),
+                execs().with_status(0));
+}
+
+#[test]
+fn env_test() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+            build = "build.rs"
+        "#)
+        .file("build.rs", r#"
+            fn main() {
+                println!("cargo:rustc-env=FOO=foo");
+            }
+        "#)
+        .file("src/lib.rs", r#"
+            pub const FOO: &'static str = env!("FOO");
+        "#)
+        .file("tests/test.rs", r#"
+            extern crate foo;
+
+            #[test]
+            fn test_foo() {
+                assert_eq!("foo", foo::FOO);
+            }
+        "#);
+    assert_that(p.cargo_process("test").arg("-v"),
+                execs().with_stderr(format!("\
+[COMPILING] foo v0.0.1 ({dir})
+[RUNNING] [..] build.rs [..]
+[RUNNING] `[..][/]build-script-build`
+[RUNNING] [..] --cfg foo[..]
+[RUNNING] [..] --cfg foo[..]
+[RUNNING] [..] --cfg foo[..]
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] `[..][/]foo-[..][EXE]`
+[RUNNING] `[..][/]test-[..][EXE]`
+[DOCTEST] foo
+[RUNNING] [..] --cfg foo[..]", dir = p.url()))
+                       .with_stdout("
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
+
+
+running 1 test
+test test_foo ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+"));
+}
+
 #[test]
 fn flags_go_into_tests() {
     let p = project("foo")
@@ -1816,7 +1892,7 @@ fn fresh_builds_possible_with_link_libs() {
             rustc-flags = \"-l z -L ./\"
         ", target))
         .file("build.rs", "");
+
     assert_that(p.cargo_process("build").arg("-v"),
                 execs().with_status(0).with_stderr("\
 [COMPILING] foo v0.5.0 ([..]
@@ -1857,7 +1933,7 @@ fn fresh_builds_possible_with_multiple_metadata_overrides() {
             e = \"\"
         ", target))
         .file("build.rs", "");
+
     assert_that(p.cargo_process("build").arg("-v"),
                 execs().with_status(0).with_stderr("\
 [COMPILING] foo v0.5.0 ([..]